package main

import (
	"encoding/csv"
	"fmt"
	"os"
	"strings"

	"golang.org/x/text/encoding/simplifiedchinese"
	"golang.org/x/text/transform"
	"google.golang.org/protobuf/compiler/protogen"
)

const (
	errorsPackage       = protogen.GoImportPath("gitee.com/dhcy/aithu-protoc/errors")
	enumNameRequired    = "Reason"              // 枚举命名需要带有字段
	enumModuleNumKey    = "+module="            // 模块号key
	enumSourceKey       = "+source="            // 来源key
	enumValueDeprecated = "+deprecated"         // 枚举值废弃标识
	dateFormat          = "2006-01-02 15:04:05" // 构建时间格式
	maxProtoNum         = 99999                 // 最大协议号
)

var (
	tableTitle = []string{"code", "message", "deprecated", "new_message"} // 表抬头
)

// generateFile generates a _table.pb.go file containing aithu-protoc errors definitions.
func generateFile(gen *protogen.Plugin, file *protogen.File) {
	if len(file.Enums) == 0 {
		return
	}
	filename := file.GeneratedFilenamePrefix + "_table.csv"
	f, err := os.Create(filename)
	if err != nil {
		panic(fmt.Sprintf("创建文件 %s 失败 err: %s", filename, err))
	}
	// 解决中文乱码
	writer := csv.NewWriter(transform.NewWriter(f, simplifiedchinese.GBK.NewEncoder()))
	err = writer.Write(tableTitle)
	if err != nil {
		panic(fmt.Sprintf("写入 %s 文件表头失败 err: %s", filename, err))
	}
	generateFileContent(gen, file, writer)
}

// generateFileContent generates the aithu-protoc errors definitions, excluding the package statement.
func generateFileContent(gen *protogen.Plugin, file *protogen.File, w *csv.Writer) {
	if len(file.Enums) == 0 {
		return
	}
	index := 0
	for _, enum := range file.Enums {
		skip := genErrorsReason(gen, file, w, enum)
		if !skip {
			index++
		}
	}
	// If all enums do not contain 'errors.code', the current file is skipped
	if index > 0 {
		w.Flush()
	}
}

func genErrorsReason(gen *protogen.Plugin, file *protogen.File,
	w *csv.Writer, enum *protogen.Enum) bool {

	// 枚举定义变量
	enumName := string(enum.Desc.Name())          // 枚举名称
	enumComments := string(enum.Comments.Leading) // 枚举注释

	// 获取匹配符合规则的枚举前缀
	enumNamePrefix := mateEnumName(enumName)
	if enumNamePrefix == "" {
		return true
	}

	// 获取source信息
	source := getEnumKeyComment(enumComments, enumSourceKey)
	if source == "" {
		panic(fmt.Sprintf("枚举%s注释中没有定义%s来源 请检查", enumName, enumSourceKey))
	}
	// 获取module号
	moduleNum := getEnumKeyComment(enumComments, enumModuleNumKey)
	if moduleNum == "" {
		panic(fmt.Sprintf("枚举%s注释中没有定义%s模块号 请检查", enumName, enumModuleNumKey))
	}

	for _, v := range enum.Values {
		// 枚举值变量
		enumValueComments := string(v.Comments.Leading) // 枚举值注释
		enumValueName := string(v.Desc.Name())          // 枚举值名称
		enumValueNum := int32(v.Desc.Number())          // 枚举值协议号

		// 排除协议号为0的枚举值
		if enumValueNum == 0 {
			continue
		}

		// 协议号为负数检查
		if enumValueNum < 0 {
			panic(fmt.Sprintf("枚举 %s 中协议 %s = %d 协议号不能为负数", enumName, enumValueName, enumValueNum))
		}
		// 协议号超过最大值检查
		if enumValueNum > maxProtoNum || enumValueNum < 0 {
			panic(fmt.Sprintf("枚举 %s 中协协议 %s = %d 协议号超过最大值 %d", enumName, enumValueName, enumValueNum,
				maxProtoNum))
		}

		// 获取协议类型
		protoTyp := getProtoTyp(enumValueName)
		if protoTyp == "" {
			panic(fmt.Sprintf("枚举 %s 中协议 %s = %d 类型定义错误 请检查", enumName, enumValueName, enumValueNum))
		}

		// 获取注释信息
		message := getEnumValueComments(enumValueComments)
		if message == "" {
			panic(fmt.Sprintf("枚举 %s 中的协议 %s = %d 注释格式错误 请检查", enumName, enumValueName, enumValueNum))
		}

		// 构建code
		code := buildErrorCode(protoTyp, source, moduleNum, enumValueNum)

		// 获取枚举值废弃状态
		deprecated := "否"
		if strings.Contains(enumValueComments, enumValueDeprecated) {
			deprecated = "是"
		}
		err := w.Write([]string{code, message, deprecated})
		if err != nil {
			panic(fmt.Sprintf("写入code %s 失败 err: %s", code, err))
		}
	}
	return false
}

// getEnumKeyComment 获取枚举注释key的内容
func getEnumKeyComment(enumComments, key string) string {
	// 获取注释中的模块号
	nots := strings.Split(enumComments, "\n")
	for i := 0; i < len(nots); i++ {
		// 匹配是否包含模块key
		if strings.Contains(nots[i], key) {
			moduleNum := strings.Replace(nots[i], key, "", -1)
			return strings.Replace(moduleNum, " ", "", -1)
		}
	}
	return ""
}

// buildErrorCode 组装error code
func buildErrorCode(protoTyp, source, module string, enumNum int32) string {
	protoUpper := strings.ToUpper(protoTyp)
	sourceUpper := strings.ToUpper(source)
	return fmt.Sprintf("%s%s%s%05d", string(protoUpper[0]), string(sourceUpper[0]), module, enumNum)
}

// mateEnumName 匹配符合规则的枚举前缀
func mateEnumName(enumName string) string {
	if strings.Contains(enumName, enumNameRequired) {
		// 获取枚举前缀
		return strings.Replace(enumName, enumNameRequired, "", -1)
	}
	return ""
}

// getEnumValueComments 获取枚举值注释(排除废弃标识)
func getEnumValueComments(enumValueComments string) string {
	if len(enumValueComments) == 0 {
		return ""
	}
	replace := strings.Replace(enumValueComments, "//", "", -1)
	replace = strings.Replace(replace, "\n", "", -1)
	replace = strings.Replace(replace, " ", "", -1)
	return strings.Replace(replace, enumValueDeprecated, "", -1)
}

// getProtoTyp 获取协议类型(Error)
func getProtoTyp(enumValueName string) string {
	if !strings.Contains(enumValueName, "_") {
		return ""
	}
	typs := strings.Split(enumValueName, "_")
	return typs[0]
}

// buildFuncName 构造函数名称
func buildFuncName(enumValueName, typ, source, enumNamePrefix string) string {
	// 协议格式化
	proto := strings.Replace(enumValueName, typ, "", -1)
	proto = strings.ToLower(proto)
	proto = strings.Replace(proto, "_", " ", -1)
	proto = strings.Title(proto)
	proto = strings.Replace(proto, " ", "", -1)
	// 类型格式化
	newTyp := strings.ToLower(typ)
	newTyp = strings.Title(newTyp)
	// 来源格式化
	newSource := strings.Title(source)
	// 构建成名称
	return fmt.Sprintf("%s%s%s%s", newTyp, newSource, enumNamePrefix, proto)
}
